// iple_win32.cpp
//

#include "stdafx.h"
#include "iple_win32.h"

#define MAX_LOADSTRING 100

#define BUFSIZE 512	// default min buffer size

// DEBUG ////////////////////////////////////////

// These variables are required by debugging

static DWORD dwError = 0;
static TCHAR sDebugFile[BUFSIZE];
static HANDLE hDebugFile = INVALID_HANDLE_VALUE;

// ENVIRONMENT DEFINITIONS //////////////////////

// These variables are required by building application framework

HINSTANCE	hInst;
TCHAR		szTitle			[MAX_LOADSTRING];
TCHAR		szWindowClass	[MAX_LOADSTRING];

// These variables are required by BitBlt. 
 
static HDC			hdcWin;		// window DC 
static HDC			hdcCompat;	// compatible DC
static HBITMAP		hbmpCompat;	// bitmap handle to old DC 
static BITMAPINFO	bmpi;		// bitmap data structure 
 
// These variables are required for horizontal scrolling. 
 
static int xMinScroll;       // minimum horizontal scroll value 
static int xCurrentScroll;   // current horizontal scroll value 
static int xMaxScroll;       // maximum horizontal scroll value 
 
// These variables are required for vertical scrolling. 
 
static int yMinScroll;       // minimum vertical scroll value 
static int yCurrentScroll;   // current vertical scroll value 
static int yMaxScroll;       // maximum vertical scroll value

// These variables are required for scrolling operation

static SCROLLINFO si;		 // scroll information structure

// These functions are required by building application framework

ATOM				MyRegisterClass	(HINSTANCE hInstance);
BOOL				InitInstance	(HINSTANCE, int);
LRESULT CALLBACK	WndProc			(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About			(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	ToolDlgProc		(HWND, UINT, WPARAM, LPARAM);

// IPLE DEFINITIONS /////////////////////////////

// These variables are system environment

static TCHAR sTempPath[BUFSIZE];				// tempory path
static TCHAR sCurrentPath[BUFSIZE];				// current directory

// These variables are required by file operation

static TCHAR sImageFile[BUFSIZE];				// image file name
static TCHAR sTempFile[BUFSIZE];				// current temp file name
static HANDLE hTempFile = INVALID_HANDLE_VALUE;	// handle for current temp file

static IMAGE image;

// These variables are required by image display

static BOOL fRebuildBitmap = TRUE;				// rebuild bitmap

// These variables are required by dialogs management

static HWND	hwndTools = NULL;	// tool dialog

// These variables are required by marking status

/*
 * STYLE	10000000
 *
 * 
 MEANING OF BITS (FROM LEFT TO RIGHT)
 * Pionter
 * Select
 * Pencil
 */

static BYTE markTools = 0x70;		// tool marks

// ENVIRONMENT REALIZATION //////////////////////

// Application entrance

int APIENTRY _tWinMain(HINSTANCE hInstance,
					   HINSTANCE hPrevInstance,
					   LPTSTR    lpCmdLine,
					   int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);


	MSG msg;
	HACCEL hAccelTable;


	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_IPLE_WIN32, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);


	if (!InitInstance (hInstance, nCmdShow))
	{
		return FALSE;
	}

	// Get system environment variables ////////////

	// get temp path

	if (GetTempPath(BUFSIZE, sTempPath) == FALSE) {
		dwError = GetLastError();
		return dwError;
	}

	if (GetCurrentDirectory(BUFSIZE, sCurrentPath) == FALSE) {
		dwError = GetLastError();
		return dwError;
	}

	////////////////////////////////////////////////

	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_IPLE_WIN32));


	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int) msg.wParam;
}

// Main form register

ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_IPLE_WIN32));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_IPLE_WIN32);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

// Initialize instance

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
	HWND hWnd;

	hInst = hInstance;

	hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
	  CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

	if (!hWnd)
	{
		return FALSE;
	}

	ShowWindow(hWnd, nCmdShow);
	//AnimateWindow(hWnd, 200, AW_BLEND);
	UpdateWindow(hWnd);

	hwndTools = CreateDialog(hInst, MAKEINTRESOURCE(IDD_TOOLDIALOG), hWnd, NULL);
	ShowWindow(hwndTools, SW_SHOW);

	return TRUE;
}

// Messages holder for main form

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_CREATE:
		xMinScroll = 0;
		xMaxScroll = 0;

		yMinScroll = 0;
		yMaxScroll = 0;

		xCurrentScroll = 0;
		yCurrentScroll = 0;

		break;
	case WM_SIZE:
		{
			int xNewSize; 
			int yNewSize; 

			xNewSize = LOWORD(lParam); 
			yNewSize = HIWORD(lParam); 

			// The horizontal scrolling range is defined by 
			// (bitmap_width) - (client_width). The current horizontal 
			// scroll value remains within the horizontal scrolling range. 

			xMaxScroll = max(image.header.width-xNewSize, 0);
			xCurrentScroll = min(xCurrentScroll, xMaxScroll);
			si.cbSize = sizeof(si); 
			si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS; 
			si.nMin   = xMinScroll; 
			si.nMax   = image.header.width; 
			si.nPage  = xNewSize; 
			si.nPos   = xCurrentScroll; 
			SetScrollInfo(hWnd, SB_HORZ, &si, TRUE); 

			// The vertical scrolling range is defined by 
			// (bitmap_height) - (client_height). The current vertical 
			// scroll value remains within the vertical scrolling range. 

			yMaxScroll = max(image.header.height-yNewSize, 0);
			yCurrentScroll = min(yCurrentScroll, yMaxScroll);
			si.cbSize = sizeof(si); 
			si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS; 
			si.nMin   = yMinScroll; 
			si.nMax   = image.header.height; 
			si.nPage  = yNewSize; 
			si.nPos   = yCurrentScroll; 
			SetScrollInfo(hWnd, SB_VERT, &si, TRUE);

			// Create compatible DC //////////////////////

			if (fRebuildBitmap) {

				// Get window DC

				hdcWin = GetDC(hWnd);

				// Create a compatible DC

				hdcCompat = CreateCompatibleDC(hdcWin);

				fRebuildBitmap = FALSE;
			}

			//////////////////////////////////////////////
		} 
        break; 
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		// menu commands:
		switch (wmId)
		{
		case ID_FILE_LOAD:
			{
				OPENFILENAME ofn;
				ZeroMemory(&ofn, sizeof(ofn));
				ofn.lStructSize = sizeof(ofn);
				ofn.hwndOwner = hWnd;
				ofn.lpstrFile = sImageFile;
				ofn.lpstrFile[0] = L'\0';
				ofn.nMaxFile = sizeof(sImageFile);
				ofn.lpstrFilter = L"All\0*.*\0Bitmap\0*.bmp\0";
				ofn.nFilterIndex = 1;
				ofn.lpstrFileTitle = NULL;
				ofn.nMaxFileTitle = 0;
				ofn.lpstrInitialDir = NULL;
				ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

				if (!GetOpenFileName(&ofn)) {
					OutputDebugLine(hDebugFile, L"Fail on GetOpenFileName");
				} else if (!LoadFile(ofn.lpstrFile, sTempFile)) {
					OutputDebugLine(hDebugFile, L"Fail on LoadFile");
				}

				break;
			}
		case ID_FILE_SAVE:
			{
				OPENFILENAME ofn;
				ZeroMemory(&ofn, sizeof(ofn));
				ofn.lStructSize = sizeof(ofn);
				ofn.hwndOwner = hWnd;
				ofn.lpstrFile = sImageFile;
				ofn.lpstrFile[0] = L'\0';
				ofn.nMaxFile = sizeof(sImageFile);
				ofn.lpstrFilter = L"All\0*.*\0Bitmap\0*.bmp\0";
				ofn.nFilterIndex = 1;
				ofn.lpstrFileTitle = NULL;
				ofn.nMaxFileTitle = 0;
				ofn.lpstrInitialDir = NULL;
				ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

				if (!GetSaveFileName(&ofn)) {
					OutputDebugLine(hDebugFile, L"Fail on GetSaveFileName");
				} else if (!SaveFile(ofn.lpstrFile, sTempFile)) {
					OutputDebugLine(hDebugFile, L"Fail on SaveFile");
				}

				break;
			}
		case ID_FILE_CLOSE:
			break;
		case IDM_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
			break;
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_ERASEBKGND:
			break;
	case WM_PAINT:
		{ 
			hdc = BeginPaint(hWnd, &ps); 
			if (!Display(hdc, &image, ps.rcPaint.right, ps.rcPaint.bottom))
				OutputDebugLine(hDebugFile, L"Error on Display");
			EndPaint(hWnd, &ps); 
		} 
		break;
    case WM_HSCROLL: 
		{ 
			int xNewPos;

			switch (LOWORD(wParam)) 
			{ 
				// User clicked the scroll bar shaft left of the scroll box. 
	 
				case SB_PAGEUP: 
					xNewPos = xCurrentScroll - 50; 
					break; 
	 
				// User clicked the scroll bar shaft right of the scroll box. 
	 
				case SB_PAGEDOWN: 
					xNewPos = xCurrentScroll + 50; 
					break; 
	 
				// User clicked the left arrow. 
	 
				case SB_LINEUP: 
					xNewPos = xCurrentScroll - 5; 
					break; 
	 
				// User clicked the right arrow. 
	 
				case SB_LINEDOWN: 
					xNewPos = xCurrentScroll + 5; 
					break; 
	 
				// User dragged the scroll box. 
	 
				case SB_THUMBPOSITION: 
					xNewPos = HIWORD(wParam); 
					break; 
	 
				default: 
					xNewPos = xCurrentScroll; 
			} 
	 
			// New position must be between 0 and the screen width. 
	 
			xNewPos = min(xMaxScroll, xNewPos);
	 
			// Reset the current scroll position. 
	 
			xCurrentScroll = xNewPos; 
	 
			// Reset the scroll bar. 
	 
			si.cbSize = sizeof(si); 
			si.fMask  = SIF_POS; 
			si.nPos   = xCurrentScroll; 
			SetScrollInfo(hWnd, SB_HORZ, &si, TRUE); 
	 
			// Display

			RECT rect;
			GetClientRect(hWnd, &rect);

			if (!Display(hdcWin, &image, rect.right, rect.bottom))
				OutputDebugLine(hDebugFile, L"Error on Display");
		} 
        break; 
    case WM_VSCROLL: 
		{  
			int yNewPos;    // new position 
	 
			switch (LOWORD(wParam)) 
			{ 
				// User clicked the scroll bar shaft above the scroll box. 
	 
				case SB_PAGEUP: 
					yNewPos = yCurrentScroll - 50; 
					break; 
	 
				// User clicked the scroll bar shaft below the scroll box. 
	 
				case SB_PAGEDOWN: 
					yNewPos = yCurrentScroll + 50; 
					break; 
	 
				// User clicked the top arrow. 
	 
				case SB_LINEUP: 
					yNewPos = yCurrentScroll - 5; 
					break; 
	 
				// User clicked the bottom arrow. 
	 
				case SB_LINEDOWN: 
					yNewPos = yCurrentScroll + 5; 
					break; 
	 
				// User dragged the scroll box. 
	 
				case SB_THUMBPOSITION: 
					yNewPos = HIWORD(wParam); 
					break; 
	 
				default: 
					yNewPos = yCurrentScroll; 
			} 

			yNewPos = min(yMaxScroll, yNewPos);
	 
			yCurrentScroll = yNewPos; 
	 
			// Reset the scroll bar. 
	 
			si.cbSize = sizeof(si); 
			si.fMask  = SIF_POS; 
			si.nPos   = yCurrentScroll; 
			SetScrollInfo(hWnd, SB_VERT, &si, TRUE); 
	 
			RECT rect;
			GetClientRect(hWnd, &rect);

			if (!Display(hdcWin, &image, rect.right, rect.bottom))
				OutputDebugLine(hDebugFile, L"Error on Display");
		} 
        break; 
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// Message holder for About Box

INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

/////////////////////////////////////////////////

INT_PTR CALLBACK ToolDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) 
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		{
			HWND hPointer = GetDlgItem(hDlg, IDC_RADIO1);
			// set hPointer to selected -- msdn
			return (INT_PTR)TRUE;
		}
	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}